package org.lboot.rbac.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.lboot.core.auth.anno.CheckRole;
import org.lboot.core.constant.CommonConst;
import org.lboot.core.domain.ResponseDTO;
import org.lboot.core.exception.AsyncBusinessException;
import org.lboot.core.exception.BusinessException;
import org.lboot.llog.anno.ApiLog;
import org.lboot.rbac.constant.RbacAuthConst;
import org.lboot.rbac.module.role.SysRole;
import org.lboot.rbac.module.role.SysRoleService;
import org.lboot.rbac.module.user.SysUser;
import org.lboot.rbac.module.user.SysUserService;
import org.lboot.rbac.params.LoginParams;
import org.lboot.rbac.provider.RbacAuthServiceImpl;
import org.lboot.rbac.util.RbacUtil;
import org.lboot.starter.anno.FormLock;
import org.lboot.starter.plugin.rsr.RsrTree;
import org.lboot.starter.redis.RedisCache;
import org.springframework.http.HttpStatus;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author kindear
 * 系统鉴权控制器
 */

@Slf4j
@RestController
@RequestMapping("system")
@Api(tags = "系统鉴权")
@RsrTree("系统管理")
@AllArgsConstructor
public class AuthController {

    RedisCache redisCache;

    SysUserService userService;

    SysRoleService roleService;

    RbacAuthServiceImpl authService;

    ThreadPoolTaskExecutor threadExecutor;

    @GetMapping(value = "captcha")
    @ApiOperation(value = "获取图片验证码",notes = "")
    public ResponseDTO<Map<String,String>> getCaptcha(){
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(116, 36,4,10);
        log.info("生成的图片验证码:"+lineCaptcha.getCode());
        // 生成和验证码对应的唯一UUID
        String uuid = IdUtil.randomUUID();
        // 将 uuid-code - 生命周期 存入Redis
        redisCache.setCacheObject(CommonConst.CAPTCHA_CODE_KEY.concat(uuid), lineCaptcha.getCode(), CommonConst.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        Map<String,String> captchaMap = new HashMap<>(2);
        captchaMap.put("uuid",CommonConst.CAPTCHA_CODE_KEY.concat(uuid));
        captchaMap.put("img",lineCaptcha.getImageBase64Data());
        return ResponseDTO.succData(captchaMap);
    }

    // 支持登录拓展
    @PostMapping("login")
    @ApiLog(value = "系统鉴权",ignoreRequest = 1)
    @ApiOperation(value = "用户登录",notes = "")
    public ResponseDTO<Object> login(@RequestBody @Validated LoginParams loginParams){
        String captchaCode = redisCache.getCacheObject(loginParams.getUuid());
        // 如果为空
        if(Validator.isEmpty(captchaCode)){
            throw new BusinessException(HttpStatus.INTERNAL_SERVER_ERROR,"验证码过期");
        }
        log.debug("UUID:["+loginParams.getUuid()+"]对应的验证码为:"+captchaCode);
        // 清除缓存中的图片
        redisCache.deleteObject(CommonConst.CAPTCHA_CODE_KEY.concat(loginParams.getUuid()));
        if(!captchaCode.equals(loginParams.getCode())){
            throw new BusinessException(HttpStatus.BAD_REQUEST,"验证码错误");
        }

        // 根据用户名称获取
        CompletableFuture<SysUser> queryUserName = CompletableFuture.supplyAsync(()->{
            SysUser user;
            Optional<SysUser> userOp = userService.getByUserName(loginParams.getUserName());
            if (!userOp.isPresent()) {
                return null;
            }
            user = userOp.get();
            // 比对密码
            if (!loginParams.getPassword().equals(user.getPassword())){
                return null;
            }
            return user;
        },threadExecutor);

        // 根据用户手机号获取
        CompletableFuture<SysUser> queryMobile = CompletableFuture.supplyAsync(()->{
            List<SysUser> users = userService.getByMobile(loginParams.getUserName());
            // 根据手机号码查询结果不唯一->则使用手机号登录
            if (users.size() != 1){
                return null;
            }
            // 获取用户信息
            SysUser user = users.get(0);
            // 比对密码
            if (!loginParams.getPassword().equals(user.getPassword())){
                return null;
            }
            return user;
        },threadExecutor);

        CompletableFuture<SysUser> future = CompletableFuture.allOf(queryUserName,queryMobile).thenApplyAsync(s -> {
            if (Validator.isNotEmpty(queryUserName.join())){
                return queryUserName.join();
            }
            if (Validator.isNotEmpty(queryMobile.join())){
                return queryMobile.join();
            }
            throw new AsyncBusinessException(HttpStatus.NOT_FOUND,"请检查账号密码是否正确");
        });

        SysUser user = future.join();

        // 账户禁用查询
        if (authService.isBan(user.getId())){
            return ResponseDTO.wrap(HttpStatus.FORBIDDEN,"账户已被禁用");
        }

        //执行登录
        StpUtil.login(user.getId());
        // 构建Map
        Map<String,Object> resMap = new HashMap<>(4);
        resMap.put("id",user.getId());
        resMap.put("userName",user.getUserName());
        resMap.put("nickName",user.getNickName());
        resMap.put("token",StpUtil.getTokenValue());
        return ResponseDTO.succData(resMap);
    }


    @SneakyThrows
    @GetMapping(value = {"userInfo","user/info"})
    @ApiOperation(value = "用户信息",notes = "")
    public ResponseDTO<Object> userInfoQuery() {
        // 获取登录ID -- 只有登录态用户才可以调用
        Long loginId = StpUtil.getLoginIdAsLong();
        // 定义返回结果
        Map<String,Object> resultMap;
        SysUser user = userService.getOneUnwrap(loginId);
        resultMap = BeanUtil.beanToMap(RbacUtil.desensitize(user));
        Set<SysRole> roleList = new HashSet<>(userService.getRoles(user.getId()));
        // 定义权限列表
        List<String> perms = new ArrayList<>();
        // 定义角色列表
        List<String> roles = new ArrayList<>();
        for (SysRole role:roleList){
            roles.add(role.getRoleKey());
            perms.addAll(role.getPermissions());
        }
        // 去重
        perms = perms.stream().distinct().collect(Collectors.toList());
        resultMap.put("permission",perms);
        resultMap.put("roles",roles);
        resultMap.put("isAdmin", authService.isAdmin());
        // 查询是否禁用
        resultMap.put("isBan", authService.isBan(loginId));
        return ResponseDTO.succData(resultMap);
    }

    @ApiLog("RBAC")
    @PostMapping("logout")
    @ApiOperation(value = "用户登出",notes = "")
    public ResponseDTO<Object> logout(){
        StpUtil.logout();
        return ResponseDTO.succMsg("登出成功");
    }


    @ApiLog("RBAC")
    @PostMapping("ban/{userId}")
    @ApiOperation(value = "用户封禁",notes = "")
    @CheckRole(orPerm = "rbac:user:ban")
    public ResponseDTO<Object> userBan(@PathVariable("userId") Long userId,
                                       @RequestParam("hour") Integer hour){
        authService.doBan(userId,hour);
        return ResponseDTO.succMsg("账户已被禁用");
    }


    @ApiLog("RBAC")
    @DeleteMapping("ban/{userId}")
    @ApiOperation(value = "用户解禁",notes = "")
    @CheckRole(orPerm = "rbac:user:free")
    public ResponseDTO<Object> userFree(@PathVariable("userId") Long userId){
        redisCache.deleteObject(RbacAuthConst.BAN_CODE.concat(userId.toString()));
        return ResponseDTO.succMsg("解封成功");
    }
}
